home *** CD-ROM | disk | FTP | other *** search
- /* FTP Server state machine - see RFC 959 */
-
- #define LINELEN 128 /* Length of command buffer */
-
- #include <stdio.h>
- #include "machdep.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "timer.h"
- #include "tcp.h"
- #include "ftp.h"
-
- /* Command table */
- static char *commands[] = {
- "user",
- #define USER_CMD 0
- "acct",
- #define ACCT_CMD 1
- "pass",
- #define PASS_CMD 2
- "type",
- #define TYPE_CMD 3
- "list",
- #define LIST_CMD 4
- "cwd",
- #define CWD_CMD 5
- "dele",
- #define DELE_CMD 6
- "name",
- #define NAME_CMD 7
- "quit",
- #define QUIT_CMD 8
- "retr",
- #define RETR_CMD 9
- "stor",
- #define STOR_CMD 10
- "port",
- #define PORT_CMD 11
- "nlst",
- #define NLST_CMD 12
- "pwd",
- #define PWD_CMD 13
- "xpwd", /* For compatibility with 4.2BSD */
- #define XPWD_CMD 14
- NULLCHAR
- };
-
- /* Response messages */
- static char banner[] = "220 %s FTP Ready\r\n";
- static char badcmd[] = "500 Unknown command\r\n";
- static char nopass[] = "202 Password not needed\r\n";
- static char logged[] = "230 Logged in\r\n";
- static char typeok[] = "200 Type OK\r\n";
- static char cwdok[] = "250 CWD OK\r\n";
- static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
- static char badtype[] = "501 Unknown type\r\n";
- static char badport[] = "501 Bad port syntax\r\n";
- static char unimp[] = "502 Command not yet implemented\r\n";
- static char bye[] = "221 Goodbye!\r\n";
- static char nodir[] = "553 Can't read directory\r\n";
- static char cantopen[] = "550 Can't open file\r\n";
- static char sending[] = "150 Opening data connection for %s %s\r\n";
- static char cantmake[] = "553 Can't create file\r\n";
- static char portok[] = "200 Port command okay\r\n";
- static char rxok[] = "226 File received OK\r\n";
- static char txok[] = "226 File sent OK\r\n";
-
- static struct tcb *ftp_tcb;
-
- /* Start up FTP service */
- ftp_start(argc,argv)
- int argc;
- char *argv[];
- {
- struct socket lsocket;
- void r_ftp(),s_ftp();
-
- lsocket.address = ip_addr;
- if(argc < 2)
- lsocket.port = FTP_PORT;
- else
- lsocket.port = atoi(argv[1]);
-
- ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_ftp,NULLVFP,s_ftp,0,(int *)NULL);
- }
- ftp_stop()
- {
- if(ftp_tcb != NULLTCB)
- close_tcp(ftp_tcb);
- }
- /* FTP server control channel connection state change upcall handler */
- static
- void
- s_ftp(tcb,old,new)
- struct tcb *tcb;
- char old,new;
- {
- extern char hostname[];
- struct ftp *ftp,*ftp_create();
- void ftp_delete();
- char *inet_ntoa(),*pwd();
-
- switch(new){
- #ifdef QUICKSTART
- case SYN_RECEIVED:
- #else
- case ESTABLISHED:
- #endif
- if((ftp = ftp_create(LINELEN)) == NULLFTP){
- /* No space, kill connection */
- close_tcp(tcb);
- return;
- }
- ftp->control = tcb; /* Downward link */
- tcb->user = (int *)ftp; /* Upward link */
-
- /* Set default data port */
- ftp->port.address = tcb->conn.remote.address;
- ftp->port.port = FTPD_PORT;
-
- /* Note current directory */
- #ifndef AMIGA
- ftp->cd = pwd();
- #endif
- log(tcb,"open FTP");
- tprintf(ftp->control,banner,hostname);
- break;
- case CLOSE_WAIT:
- close_tcp(tcb);
- break;
- case CLOSED:
- log(tcb,"close FTP");
- if((ftp = (struct ftp *)tcb->user) != NULLFTP)
- ftp_delete(ftp);
- /* Check if server is being shut down */
- if(tcb == ftp_tcb)
- ftp_tcb = NULLTCB;
- del_tcp(tcb);
- break;
- }
- }
-
- /* FTP control channel receiver upcall handler */
- static
- void
- r_ftp(tcb,cnt)
- struct tcb *tcb;
- int16 cnt;
- {
- register struct ftp *ftp;
- char *index(),c;
- struct mbuf *bp;
- void docommand();
-
- if((ftp = (struct ftp *)tcb->user) == NULLFTP){
- /* Unknown connection, just kill it */
- close_tcp(tcb);
- return;
- }
- switch(ftp->state){
- case COMMAND_STATE:
- /* Assemble an input line in the session buffer. Return if incomplete */
- recv_tcp(tcb,&bp,0);
- while(pullup(&bp,&c,1) == 1){
- switch(c){
- case '\r': /* Strip cr's */
- continue;
- case '\n': /* Complete line; process it */
- ftp->buf[ftp->cnt] = '\0';
- docommand(ftp);
- ftp->cnt = 0;
- break;
- default: /* Assemble line */
- if(ftp->cnt != LINELEN-1)
- ftp->buf[ftp->cnt++] = c;
- break;
- }
- }
- /* else no linefeed present yet to terminate command */
- break;
- case SENDING_STATE:
- case RECEIVING_STATE:
- /* Leave commands pending on receive queue until
- * present command is done
- */
- break;
- }
- }
-
- /* FTP server data channel connection state change upcall handler */
- void
- s_ftpd(tcb,old,new)
- struct tcb *tcb;
- char old,new;
- {
- struct ftp *ftp;
- #ifndef CPM
- #ifndef AMIGA
- char *cdsave;
- #endif
- #endif
-
- if((ftp = (struct ftp *)tcb->user) == NULLFTP){
- /* Unknown connection, kill it */
- close_tcp(tcb);
- return;
- }
- switch(new){
- case FINWAIT2:
- case TIME_WAIT:
- if(ftp != NULLFTP && ftp->state == SENDING_STATE){
- /* We've received an ack of our FIN, so
- * send a completion message on the control channel
- */
- ftp->state = COMMAND_STATE;
- tprintf(ftp->control,txok);
- /* Kick command parser if something is waiting */
- if(ftp->control->rcvcnt != 0)
- r_ftp(ftp->control,ftp->control->rcvcnt);
- }
- break;
- case CLOSE_WAIT:
- close_tcp(tcb);
- if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
- /* End of file received on incoming file */
- #ifdef CPM
- if(ftp->type == ASCII_TYPE)
- putc(CTLZ,ftp->fp);
- #endif
- #ifndef CPM
- #ifndef AMIGA
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Switch to user's directory*/
- #endif
- #endif
- fclose(ftp->fp);
- #ifndef CPM
- #ifndef AMIGA
- if(cdsave != NULLCHAR){
- chdir(cdsave); /* And back */
- free(cdsave);
- }
- #endif
- #endif
- ftp->fp = NULLFILE;
- ftp->state = COMMAND_STATE;
- tprintf(ftp->control,rxok);
- /* Kick command parser if something is waiting */
- if(ftp->control->rcvcnt != 0)
- r_ftp(ftp->control,ftp->control->rcvcnt);
- }
- break;
- case CLOSED:
- if(ftp != NULLFTP)
- ftp->data = NULLTCB;
- del_tcp(tcb);
- break;
- }
- }
-
- /* Parse and execute ftp commands */
- static
- void
- docommand(ftp)
- register struct ftp *ftp;
- {
- void r_ftpd(),t_ftpd(),s_ftpd();
- char *cmd,*arg,*cp,**cmdp;
- char *index(),*malloc(),*strcpy();
- struct socket dport;
- #ifndef CPM
- #ifndef AMIGA
- FILE *dir();
- char *cdsave;
- #endif
- #endif
-
- cmd = ftp->buf;
- if(ftp->cnt == 0){
- /* Can't be a legal FTP command */
- tprintf(ftp->control,badcmd);
- return;
- }
- cmd = ftp->buf;
-
- /* Translate entire buffer to lower case */
- for(cp = cmd;*cp != '\0';cp++)
- *cp = tolower(*cp);
-
- /* Find command in table; if not present, return syntax error */
- for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
- if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
- break;
- if(*cmdp == NULLCHAR){
- tprintf(ftp->control,badcmd);
- return;
- }
- arg = &cmd[strlen(*cmdp)];
- while(*arg == ' ')
- arg++;
- /* Execute specific command */
- switch(cmdp-commands){
- case USER_CMD:
- if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
- close_tcp(ftp->control);
- break;
- }
- strcpy(ftp->username,arg);
- tprintf(ftp->control,logged);
- break;
- case TYPE_CMD:
- switch(*arg){
- case 'a': /* Ascii */
- ftp->type = ASCII_TYPE;
- tprintf(ftp->control,typeok);
- break;
- case 'b': /* Binary */
- case 'i': /* Image */
- ftp->type = IMAGE_TYPE;
- tprintf(ftp->control,typeok);
- break;
- default: /* Invalid */
- tprintf(ftp->control,badtype);
- break;
- }
- break;
- case QUIT_CMD:
- tprintf(ftp->control,bye);
- close_tcp(ftp->control);
- break;
- case RETR_CMD:
- /* Disk operation; return ACK now */
- tcp_output(ftp->control);
- #ifndef CPM
- #ifndef AMIGA
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Switch to user's directory*/
- #endif
- #endif
- ftp->fp = fopen(arg,"r");
- #ifndef CPM
- #ifndef AMIGA
- chdir(cdsave); /* And back */
- free(cdsave);
- #endif
- #endif
- if(ftp->fp == NULLFILE){
- tprintf(ftp->control,cantopen);
- } else {
- log(ftp->control,"RETR %s/%s",ftp->cd,arg);
- dport.address = ip_addr;
- dport.port = FTPD_PORT;
- ftp->state = SENDING_STATE;
- tprintf(ftp->control,sending,"RETR",arg);
-
- /* This hack is just so we can talk to ourselves */
- ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
-
- ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
- }
- break;
- case STOR_CMD:
- /* Disk operation; return ACK now */
- tcp_output(ftp->control);
- #ifndef CPM
- #ifndef AMIGA
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Switch to user's directory */
- #endif
- #endif
- ftp->fp = fopen(arg,"w");
- #ifndef CPM
- #ifndef AMIGA
- chdir(cdsave); /* And back */
- free(cdsave);
- #endif
- #endif
- if(ftp->fp == NULLFILE){
- tprintf(ftp->control,cantmake);
- } else {
- log(ftp->control,"STOR %s/%s",ftp->cd,arg);
- dport.address = ip_addr;
- dport.port = FTPD_PORT;
- ftp->state = RECEIVING_STATE;
- tprintf(ftp->control,sending,"STOR",arg);
-
- /* This hack is just so we can talk to ourselves */
- ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
- 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
-
- ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
- 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
- }
- break;
- case PORT_CMD:
- if(pport(&ftp->port,arg) == -1){
- tprintf(ftp->control,badport);
- } else {
- tprintf(ftp->control,portok);
- }
- break;
- /* #ifndef CPM */
- #ifndef AMIGA
- case LIST_CMD:
- /* Disk operation; return ACK now */
- tcp_output(ftp->control);
-
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Switch to user's directory */
- ftp->fp = dir(arg,1);
- chdir(cdsave); /* And back */
- free(cdsave);
-
- if(ftp->fp == NULLFILE){
- tprintf(ftp->control,nodir);
- break;
- }
- dport.address = ip_addr;
- dport.port = FTPD_PORT;
- ftp->state = SENDING_STATE;
- tprintf(ftp->control,sending,"LIST",arg);
-
- /* This hack is just so we can talk to ourselves */
- ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
-
- ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
- break;
- case NLST_CMD:
- /* Disk operation; return ACK now */
- tcp_output(ftp->control);
-
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Switch to user's directory */
- ftp->fp = dir(arg,0);
- chdir(cdsave); /* And back */
- free(cdsave);
-
- if(ftp->fp == NULLFILE){
- tprintf(ftp->control,nodir);
- break;
- }
- dport.address = ip_addr;
- dport.port = FTPD_PORT;
- ftp->state = SENDING_STATE;
- tprintf(ftp->control,sending,"NLST",arg);
-
- /* This hack is just so we can talk to ourselves */
- ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
-
- ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
- 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
- break;
- case CWD_CMD:
- tcp_output(ftp->control); /* Disk operation; return ACK now */
-
- cdsave = pwd(); /* Save current directory */
- chdir(ftp->cd); /* Go to user's context */
- if(chdir(arg) == 0){ /* Attempt switch */
- /* Succeeded, record in control block */
- free(ftp->cd);
- ftp->cd = pwd();
- tprintf(ftp->control,cwdok);
- } else {
- /* Failed, don't change anything */
- tprintf(ftp->control,nodir);
- }
- chdir(cdsave); /* Go back */
- free(cdsave);
- break;
- case XPWD_CMD:
- case PWD_CMD:
- tprintf(ftp->control,pwdmsg,ftp->cd);
- break;
- #else
- case LIST_CMD:
- case NLST_CMD:
- case CWD_CMD:
- case XPWD_CMD:
- case PWD_CMD:
- #endif
- case ACCT_CMD:
- case DELE_CMD:
- tprintf(ftp->control,unimp);
- break;
- case PASS_CMD:
- tprintf(ftp->control,nopass);
- break;
- }
- }
- static
- int
- pport(sock,arg)
- struct socket *sock;
- char *arg;
- {
- int32 n;
- int atoi(),i;
-
- n = 0;
- for(i=0;i<4;i++){
- n = atoi(arg) + (n << 8);
- if((arg = index(arg,',')) == NULLCHAR)
- return -1;
- arg++;
- }
- sock->address = n;
- n = atoi(arg);
- if((arg = index(arg,',')) == NULLCHAR)
- return -1;
- arg++;
- n = atoi(arg) + (n << 8);
- sock->port = n;
- return 0;
- }
-